using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Controls;
using Microsoft.Practices.CompositeUI.SmartParts;
using Microsoft.Practices.CompositeUI.Utility;

namespace SCSFContrib.CompositeUI.WPF.Workspaces
{
	/// <summary>
	/// Implements an <see cref="IWorkspace"/> that displays smart parts layered like a deck.
	/// </summary>
	/// <remarks>
	/// <para>
	/// Smart parts in a <c>DeckWorkspace</c> are layered, with only one visible at any one time. Hiding the active smart part will display the
	/// smart part "underneath" it. If there is only one smart part in the workspace, hiding it will have no effect. Closing the last smart part
	/// will cause the workspace to show nothing.
	/// </para>
	/// </remarks>
	/// <example>
	/// The following example shows how a <c>DeckWorkspace</c> can be declared in XAML:
	/// <code>
	/// <![CDATA[
	///	<cab:DeckWorkspace x:Name="_deckWorkspace"/>
	/// ]]>
	/// </code>
	/// </example>
	/// <example>
	/// The following example shows how smart parts can be manipulated within a <c>DeckWorkspace</c>:
	/// <code>
	/// _deckWorkspace.Show(smartPart1);
	/// _deckWorkspace.Show(smartPart2);
	/// //smartPart1 will again be visible after hiding smartPart2
	/// _deckWorkspace.Hide(smartPart2);
	/// </code>
	/// </example>
	public class DeckWorkspace : ContentControlWorkspace<object, SmartPartInfo>
	{
		/// <summary>
		/// Shows <paramref name="smartPart"/> in this <c>DeckWorkspace</c>.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part that should be shown.
		/// </param>
		/// <param name="smartPartInfo">
		/// Ignored.
		/// </param>
		public override void OnShow(object smartPart, SmartPartInfo smartPartInfo)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");
			Guard.ArgumentNotNull(smartPartInfo, "smartPartInfo");
			UpdateContent(UpdateReason.Other);
			Activate(smartPart);
		}

		/// <summary>
		/// Activates <paramref name="smartPart"/>, ensuring it is visible in this <c>DeckWorkspace</c>.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to be activated.
		/// </param>
		public override void OnActivate(object smartPart)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");

			if (!InnerSmartParts.Contains(smartPart))
			{
				throw new ArgumentException("smartPart is not present in this workspace.", "smartPart");
			}

			MoveSmartPartInStack(smartPart, StackLocation.TopOfStack);
			UpdateContent(UpdateReason.Other);
		}

		/// <summary>
		/// Hides <paramref name="smartPart"/> in this <c>DeckWorkspace</c>.
		/// </summary>
		/// <remarks>
		/// <para>
		/// If <paramref name="smartPart"/> is the only smart part in the workspace, it will still be visible after invoking this method.
		/// </para>
		/// </remarks>
		/// <param name="smartPart">
		/// The smart part to be hidden.
		/// </param>
		public override void OnHide(object smartPart)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");

			if (!InnerSmartParts.Contains(smartPart))
			{
				throw new ArgumentException("smartPart is not present in this workspace.", "smartPart");
			}

			MoveSmartPartInStack(smartPart, StackLocation.BottomOfStack);
			UpdateContent(UpdateReason.Other);
		}

		/// <summary>
		/// Closes <paramref name="smartPart"/> in this <c>DeckWorkspace</c>.
		/// </summary>
		/// <param name="smartPart">
		/// The smart part to be closed.
		/// </param>
		public override void OnClose(object smartPart)
		{
			Guard.ArgumentNotNull(smartPart, "smartPart");

			if (!InnerSmartParts.Contains(smartPart))
			{
				throw new ArgumentException("smartPart is not present in this workspace.", "smartPart");
			}

			MoveSmartPartInStack(smartPart, StackLocation.BottomOfStack);
			object content = UpdateContent(UpdateReason.SmartPartClosed);

			if (content != null)
			{
				//there is still at least one smart part in the workspace so make sure it is activated (the top-most one that is)
				Activate(content);
			}
		}

		private object UpdateContent(UpdateReason updateReason)
		{
			object content = null;

			//when closing, the closed smart part is still present in the InnerSmartParts collection so we need to ignore it when selecting a
			//replacement
			if ((InnerSmartParts.Count > 1) || (updateReason != UpdateReason.SmartPartClosed))
			{
				content = InnerSmartParts[InnerSmartParts.Count - 1];
			}

			Content = content;
			return content;
		}

		private void MoveSmartPartInStack(object smartPart, StackLocation newLocation)
		{
			Debug.Assert(smartPart != null);
			InnerSmartParts.Remove(smartPart);
			InnerSmartParts.Insert(newLocation == StackLocation.BottomOfStack ? 0 : InnerSmartParts.Count, smartPart);
		}

		private enum UpdateReason
		{
			Other,
			SmartPartClosed
		}

		private enum StackLocation
		{
			BottomOfStack,
			TopOfStack
		}
	}
}
